package model

import (
	"My_Blog02/utils/errmsg"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"golang.org/x/crypto/scrypt"
	"gorm.io/gorm"
)

const KeyLen = 10
const SaltLen = 8

type User struct {
	gorm.Model
	Username string `gorm:"type:varchar(20);not null"`
	Account  string `gorm:"type:varchar(20);not null"`
	Email    string `gorm:"type:varchar(20);unique;not null"`
	Password string `gorm:"type:varchar(40);not null"`
}

// 增加用户
func AddUser2(user User) (User, int) {
	//先创建用户，获取数据库生成的ID
	var code int
	user.Password, code = ScryptPwd(user.Password)
	if user.Password == "" {
		return user, code
	}
	err := db.Create(&user).Error
	if err != nil {
		return user, errmsg.ERROR
	}
	//使用生成的ID创建account
	account := fmt.Sprintf("100%06d", user.ID)
	user.Account = account

	//然后更新字段
	err = db.Model(&user).Update("account", account).Error
	if err != nil {
		return user, errmsg.ERROR
	}
	return user, errmsg.SUCCSE
}

// 删除用户
func DeleteUser2(id int) (User, int) {
	var user User
	err := db.Where("id=?", id).First(&user).Error
	if err != nil {
		return User{}, errmsg.ERROR
	}
	err = db.Delete(&user).Error
	if err != nil {
		return User{}, errmsg.ERROR
	}
	return user, errmsg.SUCCSE
}

// 修改用户
func ModifyUser2(id int, user User) (User, int) {
	//db.Model方法用来指定接下来操作的模型（表）
	user.Password, _ = ScryptPwd(user.Password)
	err := db.Model(&user).Where("id=?", id).Updates(&user).Error
	if err != nil {
		return User{}, errmsg.ERROR
	}
	//db.First(&user, id).Error 会将 gorm.Model 中定义的 ID 字段从数据库中查询出来，并赋值给 user.ID。
	err = db.First(&user, id).Error
	if err != nil {
		return User{}, errmsg.ERROR
	}
	return user, errmsg.SUCCSE
}

// 查询用户
func GetUser2(id int) (User, int) {
	var user User
	err := db.Where("id=?", id).First(&user).Error
	if err != nil {
		return User{}, errmsg.ERROR
	}
	return user, errmsg.SUCCSE
}

// 密码加密
func ScryptPwd(password string) (string, int) {
	//1.生成随机盐值（8字节固定值）
	//盐值作用：确保相同密码加密后的结果不同
	salt, err := generateSalt(SaltLen)
	if err != nil {
		return "", errmsg.ERROR
	}
	//2.使用它scrypt算法计算密码哈希
	//scrypt.Key 是核心哈希计算函数，参数决定了算法的安全性和性能：
	//N=16384：越大计算越慢，暴力破解成本越高（推荐值范围 16384-65536）
	//r=8、p=1：通常使用默认值，影响内存占用和并行计算能力
	//这些参数平衡了安全性和性能，值越大安全性越高但计算耗时越长
	HashPw, err := scrypt.Key(
		[]byte(password), //原始密码（转为字节数组）
		salt,             //随机盐值
		16384,            // N：CPU/内存成本因子（迭代次数）
		8,                //r：块大小
		1,                // p：并行因子
		KeyLen) // 生成的哈希值长度
	if err != nil {
		return "", errmsg.ERROR
	}
	//3.组合盐值和哈希值，并用Base64编码
	//base64.StdEncoding.EncodeToString() 会将二进制数据编码为 仅包含可打印字符（A-Z、a-z、0-9、+、/）的字符串
	//append(salt, HashPw...) 中的三个点（...）是变长参数语法，用于将一个切片（slice）的所有元素 “展开” 后作为参数传递给 append 函数。
	//相当于combined := append(salt, HashPw[0], HashPw[1], HashPw[2], ..., HashPw[n])
	combined := append(salt, HashPw...)                 //拼接：盐值（前8字节）+哈希值（后10字节）
	Fpwd := base64.StdEncoding.EncodeToString(combined) //转为字符串方便存储
	return Fpwd, errmsg.SUCCSE
}

// 生成随机盐值
func generateSalt(length int) ([]byte, error) {
	salt := make([]byte, length) //创建指定长度的字节数组
	_, err := rand.Read(salt)    //用加密安全的随机数填充数组
	if err != nil {
		return nil, err
	}
	return salt, nil
}

// 从存储的加密密码中提取盐值和哈希值
func ExeractSaltAndHash(encoded string) (salt []byte, hash []byte, err error) {
	//1.Base64解码存储的密码
	combined, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		return nil, nil, err
	}
	//2.拆分：前8字节是盐值，后10字节是哈希值
	if len(combined) != SaltLen+KeyLen {
		return nil, nil, errors.New("invalid password format")
	}
	salt = combined[:SaltLen]
	hash = combined[SaltLen:]
	return salt, hash, nil
}
